---
id: quickstart
title: Quickstart
---

import useBaseUrl from '@docusaurus/useBaseUrl';

ORY Kratos has several moving parts and getting everything right from the
beginning can be challenging. This getting started guide will help you install
ORY Kratos and some additional dependencies so that you can see how ORY Kratos
works.

Please be aware that this guide is not a replacement for studying the docs. You
must understand core concepts and APIs to use ORY Kratos productively. This is
merely a guide to get you set up with some examples.

<iframe width="560" height="315" src="https://www.youtube.com/embed/5t1Zr_zJc7E" frameborder="0" allowfullscreen></iframe>

## Use case: You want login and registration for your Application

This section gives you some context on what we want to achieve and what tools we
need for that. You will also learn about the network set up we picked for this
guide.

This quickstart guide operates on the assumption that we are writing a NodeJS
app called **SecureApp**. This app is using nothing fancy - some ExpressJS and a
bit of HTML Templating using Handlebars. We do want to use TypeScript but only
because it's more readable - not because we're doing anything out of the
ordinary!

You could pick any technology here, of course. This works with Swift, ReactJS,
or Angular (client side) as well as with PHP, Ruby, Python, Java (server side) -
you name it! We picked NodeJS + TypeScript because we believe it is the easiest
to understand, and because JavaScript and NodeJS are universally understood and
easy to install.

We don't really know what SecureApp should do one day. But we do know that it
will have some type of dashboard and that it needs users, therefore we need:

- Login
- Logout
- Registration
- Profile management ("update first name", "update avatar ...")
- Credentials Management ("add a new recovery email", "change password", "...")
- Account Recovery ("password reset")
- Two Factor Authentication with Google Authenticator
- "Sign in with Google" and "Sign in with GitHub"

and of course:

- A dashboard that shows "Hello {{ firstName }} {{ lastName }}, your birthday is
  on {{ birthday }}!". It is only visible when the user is signed in!

## Setup

As you might already know, ORY Kratos is API-only. It does not have a UI or HTML
Templating Engine. We will implement all the user-facing UIs (dashboard, login,
registration, ...) in our NodeJS SecureApp!

To ensure that no one can access the dashboard without prior authentication
(login), we can use a small piece of code (here ExpressJS) to do that:

```js
// Import the ORY Kratos SDK. SDKs are available for all popular programming
// languages!
//
// We will add examples for other programming languages here soon!
import { KratosPublicSDK } from '@oryd/kratos-client';

// You can use protect as a middleware for expressJS:
//
//   import express from 'express'
//   const app = express()
//   app.get("/dashboard", needsLogin, dashboard)
//
const needsLogin = (req, res, next) => {
  new KratosPublicSDK('https://public.ory-kratos')
    .whoami(req)
    .then(({ body }) => {
      req.user = { session: body };
      next();
    })
    .catch(() => {
      res.redirect('/login');
    });
};
```

:::info

ORY Kratos is not just an API, it uses cookies, HTTP redirects, Anti-CSRF Tokens
and more so you don't have to!

:::

Because our SecureApp and ORY Kratos need to share cookies, in order for
Anti-CSRF Tokens and Login Session to work, we will set up path with forwards
requests to ORY Krato's Public API. If a HTTP Request to
`https://my-secureap/.ory/kratos/public/self-service/browser/flows/login` is
made, we forward (like a proxy) the request to
`https://public.ory-kratos/self-service/browser/flows/login` and pipe the
response back to the initial HTTP Request:

```js
import express from 'express';
import request from 'request';
const app = express();

const pathPrefix = '/.ory/kratos/public';
app.use(pathPrefix + '/', (req, res) => {
  const url = 'https://public.ory-kratos' + req.url.replace(pathPrefix, '');

  // Uses the request library to forward the request to ORY Kratos
  req.pipe(request(url, { followRedirect: false })).pipe(res);
});

// ...
// app.get("/dashboard", needsLogin, dashboard)
// ...
```

ORY Kratos does not ship with an administrative user interface. You must
implement that yourself or choose the ORY Cloud offering (to be announced). In
this quickstart, we will use ORY Kratos CLI (Command Line Interface) to interact
with ORY Kratos' Administrative APIs.

The quickstart also comes with [MailSlurper](https://mailslurper.com), a mock
SMTP server the demo uses to show how e.g. email verification works.

### Clone ORY Kratos and run it in Docker

To get this example working, you will need Git and
[Docker, and Docker Compose](https://docs.docker.com/get-docker/) installed on
your system. No other dependencies are required. Before you start, make sure
that Docker has enough disk space.

:::tip

This tutorial uses Docker-Compose volumes which have reported to run out of disk
space. Check the remaining disk space using `docker system df`. If the volumes
are above the 85% threshold,
[prune old Docker objects](https://docs.docker.com/config/pruning/) before you
start!

:::

:::tip

If you encounter build errors (e.g. network timeout), make sure that the network
is running correctly and run `make docker` again. If the problem persists, feel
free to [open an issue](https://github.com/ory/kratos/issues/new/choose).

:::

Let's clone ORY Kratos and run `docker-compose`:

```shell script
git clone https://github.com/ory/kratos.git
# or if you have git+ssh set up:
#  git clone git@github.com:ory/kratos.git
cd kratos
git checkout v0.3.0-alpha.1
make quickstart

# or if you don't have make installed:
docker pull oryd/kratos:latest-sqlite
docker pull oryd/kratos-selfservice-ui-node:latest
docker-compose -f quickstart.yml -f quickstart-standalone.yml up --build --force-recreate
```

This might take a minute or two. Once the output slows down and logs indicate a
healthy system you're ready to roll! A healthy system will show something along
the lines of (the order of messages might be reversed):

```
kratos_1                      | time="2020-01-20T14:52:13Z" level=info msg="Starting the admin httpd on: 0.0.0.0:4434"
kratos_1                      | time="2020-01-20T14:52:13Z" level=info msg="Starting the public httpd on: 0.0.0.0:4433"
```

:::note

There are two important factors to get a fully functional system:

- You need to make sure that ports `4455`, `4433`, `4434`, `4436` >
  [are free](https://serverfault.com/questions/309052/check-if-port-is-open-or-closed-on-a-linux-server).
- Make sure to always use `127.0.0.1` as the hostname, never use `localhost`!
  This is important because browsers treat these two as separate domains and
  will therefore have issues with setting and using the right cookies.

:::

You might notice that no database is being used in this example. ORY Kratos
supports SQLite, PostgreSQL, MySQL, and CockroachDB as database backends. For
the quickstart, we're mounting a persistent volume to store the SQLite database
in. Future guides will explain how to set up a production system!

### Network Architecture

This demo makes use of several services / Docker Images:

1. [ORY Kratos](https://github.com/ory/kratos)
2. The **SecureApp** - an
   [example application written in NodeJS](http://github.com/ory/kratos-selfservice-ui-node)
   that implements the login, registration, logout, ..., and dashboard screen.
3. An SMTP server with which ORY Kratos can send E-Mails with. We will use
   [MailHog](https://github.com/mailhog/MailHog), a minimalistic SMTP throaway
   server with an easy UI.

To better understand how everything is wired, let's take a look at the network
configuration. This assumes that you have at least some understanding of how
Docker (Compose) Networks work:

[![User Login and Registration Network Topology](https://mermaid.ink/img/eyJjb2RlIjoiZ3JhcGggVERcblxuc3ViZ3JhcGggaG5bSG9zdCBOZXR3b3JrXVxuICAgIEJbQnJvd3Nlcl1cbiAgICBCLS0-fENhbiBhY2Nlc3MgVVJMcyB2aWEgMTI3LjAuMC4xOjQ0NTV8T0tQSE5cbiAgICBCLS0-fENhbiBhY2Nlc3MgVUkgdmlhIDEyNy4wLjAuMTo0NDM2fFNNVFBVSVxuICAgIE9LUEhOKFtTZWN1cmVBcHAgZXhwb3NlZCBhdCA6NDQ1NV0pXG4gICAgU01UUFVJKFtNYWlsU2x1cnBlciBVSSBleHBvc2VkIGF0IDo0NDM2XSlcbmVuZFxuXG5zdWJncmFwaCBkbltcIkludGVybmFsIERvY2tlciBOZXR3b3JrIChpbnRyYW5ldClcIl1cbiAgICBPS1BITi0uLT5TQVxuICAgIFNNVFBVSS0uLT5TTVRQXG4gICAgU0EtLT58UHJveGllcyBVUkxzIC8ub3J5L2tyYXRvcy9wdWJsaWMvKiB0b3xPS1xuICAgIFNBLS0-fFRhbGtzIHRvIGFuZCB2YWxpZGF0ZXMgbG9naW4gc2Vzc2lvbnMgdXNpbmd8T0tcbiAgICBPSy0tPnxTZW5kcyBtYWlsIHZpYXxTTVRQXG5cbiAgICBPS1tPUlkgS3JhdG9zXVxuICAgIFNBW1wiU2VjdXJlQXBwIChPUlkgS3JhdG9zIFNlbGZTZXJ2aWNlIFVJIE5vZGUgRXhhbXBsZSlcIl1cbiAgICBTTVRQW1wiU01UUCBTZXJ2ZXIgKE1haWxTbHVycGVyKVwiXVxuZW5kXG4iLCJtZXJtYWlkIjp7InRoZW1lIjoibmV1dHJhbCIsImZsb3djaGFydCI6eyJyYW5rU3BhY2luZyI6NjUsIm5vZGVTcGFjaW5nIjozMCwiY3VydmUiOiJiYXNpcyJ9fSwidXBkYXRlRWRpdG9yIjpmYWxzZX0)](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ3JhcGggVERcblxuc3ViZ3JhcGggaG5bSG9zdCBOZXR3b3JrXVxuICAgIEJbQnJvd3Nlcl1cbiAgICBCLS0-fENhbiBhY2Nlc3MgVVJMcyB2aWEgMTI3LjAuMC4xOjQ0NTV8T0tQSE5cbiAgICBCLS0-fENhbiBhY2Nlc3MgVUkgdmlhIDEyNy4wLjAuMTo0NDM2fFNNVFBVSVxuICAgIE9LUEhOKFtTZWN1cmVBcHAgZXhwb3NlZCBhdCA6NDQ1NV0pXG4gICAgU01UUFVJKFtNYWlsU2x1cnBlciBVSSBleHBvc2VkIGF0IDo0NDM2XSlcbmVuZFxuXG5zdWJncmFwaCBkbltcIkludGVybmFsIERvY2tlciBOZXR3b3JrIChpbnRyYW5ldClcIl1cbiAgICBPS1BITi0uLT5TQVxuICAgIFNNVFBVSS0uLT5TTVRQXG4gICAgU0EtLT58UHJveGllcyBVUkxzIC8ub3J5L2tyYXRvcy9wdWJsaWMvKiB0b3xPS1xuICAgIFNBLS0-fFRhbGtzIHRvIGFuZCB2YWxpZGF0ZXMgbG9naW4gc2Vzc2lvbnMgdXNpbmd8T0tcbiAgICBPSy0tPnxTZW5kcyBtYWlsIHZpYXxTTVRQXG5cbiAgICBPS1tPUlkgS3JhdG9zXVxuICAgIFNBW1wiU2VjdXJlQXBwIChPUlkgS3JhdG9zIFNlbGZTZXJ2aWNlIFVJIE5vZGUgRXhhbXBsZSlcIl1cbiAgICBTTVRQW1wiU01UUCBTZXJ2ZXIgKE1haWxTbHVycGVyKVwiXVxuZW5kXG4iLCJtZXJtYWlkIjp7InRoZW1lIjoibmV1dHJhbCIsImZsb3djaGFydCI6eyJyYW5rU3BhY2luZyI6NjUsIm5vZGVTcGFjaW5nIjozMCwiY3VydmUiOiJiYXNpcyJ9fSwidXBkYXRlRWRpdG9yIjpmYWxzZX0)

As already explained, we're proxying requests to ORY Kratos' Public API. We are
doing this because that way all requests are going to and coming from the same
hostname. This avoids common cross-domain issues with cookies.

## Perform registration, logout, login

Enough theory, it's time to get this thing going! Let's start by trying to open
the dashboard - **go to
[127.0.0.1:4455/dashboard](http://127.0.0.1:4455/dashboard)**. You will probably
notice that you're ending up at the login endpoint:

<img
  alt="Login screen of your secured app"
  src={useBaseUrl('img/docs/secureapp-login.png')}
/>

Looking at the network stack, you can see two redirects happening:

<img
  alt="Network trace of your secured app"
  src={useBaseUrl('img/docs/secureapp-login-ntrace.png')}
/>

The first redirect from `http://127.0.0.1:4445/dashboard` to
`http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/login` is
initiated because the browser does not have a valid authentication (login)
session yet. The redirect points to one of ORY Krato's APIs used for logging in
browser-based applications. ORY Kratos does some security checks and prepares
form data and redirects the browser to `http://127.0.0.1:4445/auth/login`,
appending a `?request=...` query parameter. The endpoint at `/auth/login` (which
belongs to our SecureApp) then fetches data important for rendering the forms
from ORY Krato's Admin API:

```shell script
$ curl http://127.0.0.1:4434/self-service/browser/flows/requests/login?request=<request-id>
{
    "id": "27aa98bc-a074-418f-96fa-8b8146050209",
    "expires_at": "2020-01-20T21:10:12.7365393Z",
    "issued_at": "2020-01-20T21:00:12.7365532Z",
    "request_url": "http://127.0.0.1:4455/self-service/browser/flows/login",
    "methods": {
        "password": {
            "method": "password",
            "config": {
                "action": "http://127.0.0.1:4455/.ory/kratos/public/auth/browser/methods/password/login?request=27aa98bc-a074-418f-96fa-8b8146050209",
                "method": "POST",
                "fields": [
                    {
                        "name": "csrf_token",
                        "type": "hidden",
                        "required": true,
                        "value": "Ii8iIEdnn12vVQ2vyz2YaHjmXMUK5eSQgw9pgENGxPjXi1PHC9gOG51x61o2GT9LGvC81ddvmNXYeLvlPxA04g=="
                    },
                    {
                        "name": "identifier",
                        "type": "text",
                        "required": true
                    },
                    {
                        "name": "password",
                        "type": "password",
                        "required": true
                    }
                ]
            }
        }
    }
}
```

This data is then rendered as an HTML form. This flow also works with Single
Page Apps (SPA) and Frameworks like Angular or ReactJS. For more details about
the specific flows (login, registration, logout, ...), head over to the
[concept](concepts/index.md) chapter.

Let's move on to the next flow - registration! Click on "Create an account",
which initiates a flow similar to the one we just used:

<img
  alt="Registration screen of your secured app"
  src={useBaseUrl('img/docs/secureapp-registration.png')}
/>

The network trace looks familiar by now:

<img
  alt="Registration with network trace screen of your secured app"
  src={useBaseUrl('img/docs/secureapp-registration-ntrace.png')}
/>

If we try to sign up using a password like `123456`, ORY Krato's password policy
will complain:

<img
  alt="Registration with network trace screen of your secured app"
  src={useBaseUrl('img/docs/secureapp-registration-pwpolicy.png')}
/>

The error message is coming directly from ORY Krato's API:

```shell script
$ curl http://127.0.0.1:4434/self-service/browser/flows/requests/registration?request=<request-id>
{
    "id": "79349cbd-c785-476a-8db8-d0d71c5b003c",
    "expires_at": "2020-01-20T21:17:00.5077381Z",
    "issued_at": "2020-01-20T21:07:00.5077527Z",
    "request_url": "http://127.0.0.1:4455/self-service/browser/flows/registration",
    "methods": {
        "password": {
            "method": "password",
            "config": {
                "action": "http://127.0.0.1:4455/.ory/kratos/public/auth/browser/methods/password/registration?request=79349cbd-c785-476a-8db8-d0d71c5b003c",
                "method": "POST",
                "fields": [
                    {
                        "name": "csrf_token",
                        "type": "hidden",
                        "required": true,
                        "value": "+ZQ8x5cVgdtt4xtPIRJXQPKMVU5c/S2Mj2MuudP32vsMME0g26oQnV/H/brcNvBjkJq1XoF3UcnUFPzcr6Eq4Q=="
                    },
                    {
                        "name": "password",
                        "type": "password",
                        "required": true
                    },
                    {
                        "name": "traits.email",
                        "type": "text",
                        "value": "hello@ory.sh"
                    },
                    {
                        "name": "traits.full_name",
                        "type": "text"
                    }
                ]
            }
        }
    }
}
```

Setting a password that doesn't violate these policies, we will be immediately
redirected to the Dashboard:

<img
  alt="SecureApp Dashboard"
  src={useBaseUrl('img/docs/secureapp-dashboard.png')}
/>

By using "logout" you will be redirected to the log in screen again an will be
able to use the credentials just set up to log in!

### Understanding how Login and Registration works

Head over to the [Self-Service Flows Chapter](self-service/flows/index.md) for a
in-depth explanation of how the individual flows work.

### Email Verification

As you've signed up, an email was sent to the email address you used. Because
the quickstart uses a fake SMTP server, the email did not arrive in your inbox.
You can retrieve the email however by opening the MailSlurper UI at
[127.0.0.1:4436](http://127.0.0.1:4436).

You should see something like this:

<img
  alt="User Email Verification"
  src={useBaseUrl('img/docs/mailslurper-quickstart.png')}
/>

If not, hard refresh the tab or click on the home icon in the menu bar.

Next, click the verification link. You will end up at the dashboard, with a
verified E-Mail Address (check the `verified` and `verified_at` field in the
JSON Payload):

<img
  alt="SecureApp Dashboard"
  src={useBaseUrl('img/docs/secureapp-verified-dashboard.png')}
/>

To re-request the verification email, fill out the form at
[127.0.0.1:4455/verify](http://127.0.0.1:4455/verify).

#### Configuration Used

You can find all configuration files for this quickstart guide in
`./contrib/quickstart` and `./quickstart.yml`. To understand what each of those
individual configuration files are doing, you must consult the other chapters of
this documentation.

:::note

To get a minimal version of ORY Kratos running, you need to set configuration
items `identity.traits.default_schema_url` and `dsn`. You should also configure
`urls.*_ui` because your users will end up at fallbacks otherwise.

:::

In the future, this guide will support more use cases such as:

- Use GitHub to login in and sign up

## Cleaning up Docker

To clean everything up, you need to bring down the Docker Compose environment
and remove all mounted volumes.

```shell script
docker-compose -f quickstart.yml down -v
docker-compose -f quickstart.yml rm -f -s -v
```
